package com.xsoft.framework.common;


import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.beanutils.PropertyUtils;
import org.hibernate.Criteria;
import org.hibernate.FlushMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.impl.CriteriaImpl;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;

import com.xsoft.framework.util.BeanUtils;
import com.xsoft.framework.util.Page;
import com.xsoft.framework.util.PageControl;


/**
 * 泛型Hibernate Dao基类 提供分页函数和若干便捷查询方法，并对返回值作了泛型类型转
 * @author 潘冬
 * 2009-7-10 上午09:57:44
 * @param <T>
 */
@SuppressWarnings("unchecked")
public class HibernateGenericDao<T> extends HibernateDaoSupport {
	
	public <T> T get(Class<T> entityClass, Serializable id) {
		T o = (T) getHibernateTemplate().get(entityClass, id);
		if (o == null)
			throw new ObjectRetrievalFailureException(entityClass, id);
		return o;
	}

	public <T> List<T> getAll(Class<T> entityClass) {
		return getHibernateTemplate().loadAll(entityClass);
	}

	public void save(Object o) {
		FlushMode oldMode = getSession().getFlushMode();
		getSession().setFlushMode(FlushMode.AUTO);
		getHibernateTemplate().saveOrUpdate(o);
		getSession().setFlushMode(oldMode);
	}

	
	public void remove(Object o) {
		Session session = getSession();
		FlushMode oldMode = session.getFlushMode();
		session.setFlushMode(FlushMode.AUTO);
		getHibernateTemplate().delete(o);
		session.setFlushMode(oldMode);
	}

	public <T> void removeById(Class<T> entityClass, Serializable id) {
		Session session = getSession();
		FlushMode oldMode = session.getFlushMode();
		session.setFlushMode(FlushMode.AUTO);
		remove(get(entityClass, id));
		session.setFlushMode(oldMode);
	}
	

	/**
	 * hql查询.
	 * 
	 * @param values
	 *            可变参数 用户可以如下四种方式使用 dao.find(hql) dao.find(hql,arg0);
	 *            dao.find(hql,arg0,arg1); dao.find(hql,new
	 *            Object[arg0,arg1,arg2])
	 */
	public List find(String hql, Object... values) {
		if (values.length == 0)
			return getHibernateTemplate().find(hql);
		else
			return getHibernateTemplate().find(hql, values);
	}
	
	public List find(String hql,PageControl page, Object... values) {
		if(page==null || !page.isUsepage()){
			return find(hql,values);
		}else{
			return (List)pagedQuery(hql,page.getPageno(),page.getPagesize(),values).getResult();
		}
	}

	/**
	 * 根据属性名和属性值查询对象.
	 * 
	 * @return 符合条件的对象列表.
	 */
	public <T> List<T> findBy(Class<T> entityClass, String name, Object value) {
		Assert.hasText(name);
		Criteria criteria = getEntityCriteria(entityClass);
		criteria.add(Restrictions.eq(name, value));
		return criteria.list();
	}

	/**
	 * 根据属性名和属性值查询对象.
	 * 
	 * @return 符合条件的唯一对象象
	 */
	public <T> T findUniqueBy(Class<T> entityClass, String name, Object value) {
		Assert.hasText(name);
		Criteria criteria = getEntityCriteria(entityClass);
		criteria.add(Restrictions.eq(name, value));
		return (T) criteria.uniqueResult();
	}

	/**
	 * 根据属名和属性以Like AnyWhere方式查询对象.
	 */
	public <T> List<T> findByLike(Class<T> entityClass, String name,
			String value) {
		Assert.hasText(name);
		Criteria criteria = getEntityCriteria(entityClass);
		criteria.add(Restrictions.like(name, value, MatchMode.ANYWHERE));
		return criteria.list();
	}

	/**
	 * 取得Entity的Criteria.
	 */
	protected <T> Criteria getEntityCriteria(Class<T> entityClass) {
		return getSession().createCriteria(entityClass);
	}

	/**
	 * 判断对象某些属性的值在数据库中不存在重复
	 * 
	 * @param names
	 *            在POJO里不能重复的属性列表,以逗号分割 如"name,loginid,password"
	 */
	public <T> boolean isNotUnique(Class<T> entityClass, Object entity,
			String names) {
		Assert.hasText(names);
		Criteria criteria = getEntityCriteria(entityClass).setProjection(
				Projections.rowCount());
		String[] nameList = names.split(",");
		try {
			// 循环加入
			for (String name : nameList) {
				criteria.add(Restrictions.eq(name, PropertyUtils.getProperty(
						entity, name)));
			}

			// 以下代码为了如果是update的情况,排除entity自身.

			// 通过Hibernate的MetaData接口取得主键名
			String idPropertyName = getSessionFactory().getClassMetadata(
					entity.getClass()).getIdentifierPropertyName();
			if (idPropertyName != null) {
				// 通过反射取得entity的主键值
				Object id = PropertyUtils.getProperty(entity, idPropertyName);
				// 如果id!=null,说明对象已存在,该操作为update,加入排除自身的判断
				if (id != null)
					criteria.add(Restrictions.not(Restrictions.eq(
							idPropertyName, id)));
			}

		} catch (IllegalAccessException e) {
			logger.error("Error when reflection on entity," + e.getMessage());
			return false;
		} catch (InvocationTargetException e) {
			logger.error("Error when reflection on entity," + e.getMessage());
			return false;
		} catch (NoSuchMethodException e) {
			logger.error("Error when reflection on entity," + e.getMessage());
			return false;
		}
		return (Integer) criteria.uniqueResult() > 0;
	}

	/**
	 * 分页查询函数，使用Criteria
	 * 
	 * @param pageNo
	 *            页号,从0开始.
	 */
	public Page pagedQuery(Criteria criteria, int pageNo, int pageSize) {
		CriteriaImpl impl = (CriteriaImpl) criteria;
		
		
		// // 先把Projection和OrderBy条件取出来,清空两者来执行Count操作
		Projection projection = impl.getProjection();
		List<CriteriaImpl.OrderEntry> orderEntries;
		try {
			orderEntries = (List) BeanUtils.getPrivateProperty(impl,
					"orderEntries");
			BeanUtils.setPrivateProperty(impl, "orderEntries", new ArrayList());
		} catch (Exception e) {
			throw new InternalError(" Runtime Exception impossibility throw ");
		}

		// 执行查询
		long totalCount = (Long) criteria.setProjection(Projections.rowCount())
				.uniqueResult();

		// 将之前的Projection和OrderBy条件重新设回去
		criteria.setProjection(projection);
		if (projection == null) {
			criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		}
		try {
			BeanUtils.setPrivateProperty(impl, "orderEntries", orderEntries);
		} catch (Exception e) {
			throw new InternalError(" Runtime Exception impossibility throw ");
		}

		// 返回分页对象
		if (totalCount < 1)
			return new Page();

		int startIndex = Page.getStartOfPage(pageNo, pageSize);
		List list = criteria.setFirstResult(startIndex).setMaxResults(pageSize)
				.list();

		return new Page(startIndex, totalCount, pageSize, list);
	}

	/**
	 * 分页查询函数，使用hql.
	 * 
	 * @param pageNo
	 *            页号,从0开始.
	 */
	public Page pagedQuery(String hql, int pageNo, int pageSize, Object... args) {
		Assert.hasText(hql);
		// 创建查询
		Query query = getSession().createQuery(hql);
		for (int i = 0; i < args.length; i++) {
			query.setParameter(i, args[i]);
		}
		String countQueryString = " select count (*) "
				+ removeSelect(removeOrders(hql));
		List countlist = getHibernateTemplate().find(countQueryString, args);
		long totalCount = (Long) countlist.get(0);

		// 返回分页对象
		if (totalCount < 1)
			return new Page();
		int startIndex = Page.getStartOfPage(pageNo, pageSize);
		List list = query.setFirstResult(startIndex).setMaxResults(pageSize)
				.list();

		return new Page(startIndex, totalCount, pageSize, list);
	}

	
	/**
	 * 考虑的不全面；请参照 pagedQuery(Criteria criteria, int pageNo, int pageSize)
	 * @param pageNo
	 * @param pageSize
	 * @param map
	 * @return
	 */
	public Page pagedQueryByMap(Class entityClass,int pageNo, int pageSize,Map map){
		
		Criteria criteria =  getEntityCriteria(entityClass);
		if (map != null)   
         {   
            Set<String> keys = map.keySet();   
            for (String key : keys)   
            {   
                if(key.indexOf("id")>0){
                	criteria.add(Restrictions.eq(key, (Integer)map.get(key)));
                }else if("starttime1".equals(key)){
                	criteria.add(Restrictions.gt(key, (String)map.get(key)));
                }else if("endtime1".equals(key)){
                	criteria.add(Restrictions.lt(key, (String)map.get(key)));
                }
                else{
                	criteria.add(Restrictions.like(key, (String)map.get(key),MatchMode.ANYWHERE));
                }
            }   
         }  
		 
		int totalCount = (Integer) criteria.setProjection(Projections.rowCount()).uniqueResult();
		criteria.setProjection(null);
		
		// 返回分页对象
		if (totalCount < 1)
			return new Page();
		
		int startIndex = Page.getStartOfPage(pageNo, pageSize);
		
		List<?> list = criteria.setFirstResult(startIndex).setMaxResults(pageSize).list();
		
		return new Page(startIndex, totalCount, pageSize, list);
	
	}	
	
	
	
	/**
	 * 去除hql的select 子句，未考虑union的情况
	 */
	private static String removeSelect(String hql) {
		Assert.hasText(hql);
		int beginPos = hql.toLowerCase().indexOf("from");
		Assert.isTrue(beginPos != -1, " hql : " + hql
				+ " must has a keyword 'from'");
		return hql.substring(beginPos);
	}

	/**
	 * 去除hql的orderby 子句
	 */
	private static String removeOrders(String hql) {
		Assert.hasText(hql);
		Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*",
				Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(hql);
		StringBuffer sb = new StringBuffer();
		while (m.find()) {
			m.appendReplacement(sb, "");
		}
		m.appendTail(sb);
		return sb.toString();
	}	
}
